package com.sukaiyi.giteejavaapi.common;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.sukaiyi.giteejavaapi.common.httpendpointfactory.*;
import com.sukaiyi.giteejavaapi.converts.HttpEndpointToRequestConverter;
import com.sukaiyi.giteejavaapi.http.HttpEventCallback;
import com.sukaiyi.giteejavaapi.http.HttpService;
import com.sukaiyi.giteejavaapi.http.model.HttpRequest;
import com.sukaiyi.giteejavaapi.http.model.HttpResponse;
import com.sukaiyi.giteejavaapi.http.nativeimpl.HttpServiceNativeImpl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @author sukaiyi
 * @date 2020/06/28
 */
public class Gitee {

    private static Gitee INSTANCE = null;
    private static final List<Class<? extends HttpEndpointFactory>> ENDPOINT_FACTORIES = Arrays.asList(
            ActivityHttpEndpointFactory.class,
            EmailHttpEndpointFactory.class,
            EnterprisesHttpEndpointFactory.class,
            GistsHttpEndpointFactory.class,
            IssuesHttpEndpointFactory.class,
            GitDataHttpEndpointFactory.class,
            LabelsHttpEndpointFactory.class,
            MilestonesHttpEndpointFactory.class,
            MiscellaneousHttpEndpointFactory.class,
            OrganizationsHttpEndpointFactory.class,
            PullRequestsHttpEndpointFactory.class,
            RepositoriesHttpEndpointFactory.class,
            SearchHttpEndpointFactory.class,
            UsersHttpEndpointFactory.class,
            WebhooksHttpEndpointFactory.class
    );
    private Map<String, HttpEndpoint> apis = null;

    private final HttpService httpService = new HttpServiceNativeImpl();

    private static final String envFilePath = "apis/http-client.private.env.json";
    private static final String envProfile = "prod";

    private static Map<String, String> env = null;

    private Gitee() {
        initEnv();
        apis = ENDPOINT_FACTORIES.stream()
                .map(ReflectUtil::newInstance)
                .map(HttpEndpointFactory::produce)
                .flatMap(List::stream)
                .collect(Collectors.toMap(HttpEndpoint::getName, e -> e));
    }

    private void initEnv() {
        if (env != null) {
            return;
        }
        synchronized (Gitee.class) {
            if (env != null) {
                return;
            }
            env = new HashMap<>();
            InputStream is = Gitee.class.getClassLoader().getResourceAsStream(envFilePath);
            if (is == null) {
                return;
            }
            try {
                BufferedReader reader = new BufferedReader(new InputStreamReader(is));
                String line;
                StringBuilder sb = new StringBuilder();
                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                }
                JSONObject object = JSONUtil.parseObj(sb.toString());
                JSONObject prodObj = object.getJSONObject(envProfile);
                for (Map.Entry<String, Object> entry : prodObj.entrySet()) {
                    env.put(entry.getKey(), String.valueOf(entry.getValue()));
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                IoUtil.close(is);
            }
        }
    }

    public static Gitee getInstance() {
        if (INSTANCE == null) {
            synchronized (Gitee.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Gitee();
                }
            }
        }
        return INSTANCE;
    }

    public void invoke(String name, Object param, HttpEventCallback callback) {
        HttpEndpoint endpoint = apis.get(name);
        Objects.requireNonNull(endpoint);
        this.invoke(endpoint, param, callback);
    }

    public void invoke(HttpEndpoint endpoint, Object param, HttpEventCallback callback) {
        Objects.requireNonNull(endpoint);
        HttpRequest request = new HttpEndpointToRequestConverter(param, env).exec(endpoint);
        httpService.invoke(request, callback);
    }

    public void invoke(HttpRequest request, HttpEventCallback callback) {
        Objects.requireNonNull(request);
        httpService.invoke(request, callback);
    }

    public CompletableFuture<HttpResponse> invoke(String name, Object param) {
        HttpEndpoint endpoint = apis.get(name);
        Objects.requireNonNull(endpoint);
        HttpRequest request = new HttpEndpointToRequestConverter(param, env).exec(endpoint);
        return httpService.invoke(request);
    }

    public CompletableFuture<HttpResponse> invoke(HttpEndpoint endpoint, Object param) {
        Objects.requireNonNull(endpoint);
        HttpRequest request = new HttpEndpointToRequestConverter(param, env).exec(endpoint);
        return httpService.invoke(request);
    }

    public CompletableFuture<HttpResponse> invoke(HttpRequest request) {
        Objects.requireNonNull(request);
        return httpService.invoke(request);
    }

    public Map<String, HttpEndpoint> getApis() {
        return apis;
    }

    public void setAccessToken(String accessToken) {
        env.put("access_token", accessToken);
    }

    public Map<String, String> getEnv() {
        return env;
    }
}
